#pragma once
#include "debug.hh"
#include <cmath>
#include <utility>  // for std::swap

namespace tue
{
    namespace float_utils
    {
        static double const tolerance = 1e-16;      // XXX: tune this

        inline bool almost(double a, double b) { return (std::abs(a-b) < tolerance); }

        // calculate hypot edge of a right triangle knowing adjacent and oppo edge
        // i.e. adj(x, y) = sqrt(x*x + y*y)
        // without overflow or underflow
        inline double hypot(double x, double y)
        {
            return ::hypot(x, y);
        }

        // calculate adjacent edge of a right triangle knowing hypot and oppo edge
        // i.e. adj(x, y) = sqrt(x*x - y*y)
        // without overflow or underflow
        inline double adj(double x, double y)
        {
            // sqrt(x**2 - y**2) == |x| * sqrt(1 - (y/x)**2)
            // make sure |y/x| < 1
            x = std::abs(x);
            y = std::abs(y);
            if (x < y) std::swap(x, y);
            y /= x;
            return x * std::sqrt(1 - y*y);
        }

        // calculate the cos(angle)
        // knowing the length two adjacent edge and opposite edge
        // without overflow or underflow
        inline double cos(double a, double b, double oppo)
        {
            // (a**2 + b**2 - oppo**2) / (2*a*b)
            // == a/(2*b) + b/(2*a) - (oppo/a) * (oppo/b) / 2
            // == (a/b + b/a - (oppo/a) * (oppo/b)) / 2
            double x = a / b;
            double y = b / a;
            a = oppo / a;
            b = oppo / b;
            return (x + y - a*b) / 2.0f;
        }
    }
    using namespace float_utils;
}

